/* On{X} Script: Change phone ringer (& notification) volume on receiving call/text from defined phone number(s)
 *
 * Developer: Gaurav Nandwani
 *
 * Description:
 *
 * This rule monitors for incoming Call/SMS from particular number(s) and  changes the ringer (and 
 * notification volume, if linked to ringer volume) profile to normal (or a defined value) & volume 
 * to 100% (or a defined value). 
 * If the media volume is changed then it notifies the user and gives option to rollback to
 * previous volume & profile.
 * 
 * Usage:
 * 
 * Change following variables as needed:
 * 
 * phoneNumbers	- Array of phone numbers which can trigger the volume change.
 *                 * Note: I used array insted of a single number as I was getting input number 
 *                   different format from different SMS applications. E.g. I was getting my number 
 *                   with country code starting with '+' or sometimes without country code & 
 *                   starting with '0'.
 *                   So keeping this in mind, you'd want to put the trigger phone number in all 
 *                   possible formats you expect or change the phone number condition such that it 
 *                   matches the phone number(s) you desire.
 *                 * Also, you may want to use the array as opportunity for adding multiple people's
 *                   phone numbers.
 * ringerVolume  - Desired ringer volume. Acceptable values: Integer between 0-100
 * ringerMode    - Desired ringer mode/profile. Possible values:  'normal', 'vibrate' or 'silent'
 */
 
// Initilizing Variables

var phoneNumbers = ['+1234567890', '0234567890', '234567890'];;
var ringerVolume = 100;
var ringerMode = 'normal';

// End of Variables Initialization

console.log('Started script: When ' + phoneNumbers[0] + 
    ' calls/texts me, then change the ring volume to ' + ringerVolume + '%.');

// Function to change volume
function changeVolume(ringerMode, ringerVolume, trigger, phoneNumber, dontSave) {
    if (!dontSave) {
        // Save current volume level before changing it
        device.localStorage.setItem('ringerInfo', JSON.stringify(
            { volume:device.audio.ringerVolume, mode:device.audio.ringerMode }));
    }
    
    // Ensure the volume level is integer
    var newVolume = parseInt(ringerVolume, 10);
    
    // Change volume/mode only if needed
    var changed = false;
    if (ringerMode != device.audio.ringerMode) {
        device.audio.ringerMode = ringerMode;
        changed = true;
    }
    if (newVolume != device.audio.ringerVolume) {
        device.audio.ringerVolume = newVolume;
        changed = true;
    }
    
    // Log volume change information
    console.info('Change Volume (' + changed + '): ' + trigger + ' from ' + phoneNumber + 
        '\nTargetted Volume: ' + ringerVolume + '%, Mode: ' + ringerMode + 
        '\nCurrent Volume: ' + device.audio.ringerVolume + '%, Mode: ' + device.audio.ringerMode);
    
    // Return volume was changed status
    return changed;
}

// Function to revert volume back to the ringer volume with user confirmation
function revertVolume(trigger, phoneNumber) {
    console.log('Started: Revert Volume: Trigger: ' + trigger + ', Phone: ' + phoneNumber);
    
    // Get the last volume saved before change
    var ringerInfo = device.localStorage.getItem('ringerInfo') || 
        { volume:device.audio.ringerVolume, mode:device.audio.ringerMode };
    ringerInfo = JSON.parse(ringerInfo);
    
    console.log('Revert Volume: Last Ringer Volume: ' + ringerInfo.volume + '% Mode: ' + ringerInfo.mode + 
        '\nCurrent Ringer Volume: ' + device.audio.ringerVolume + '% Mode: ' + device.audio.ringerMode);
    
    // Proceed only if last saved volume is not equal to current volume
    if (ringerInfo.volume != device.audio.ringerVolume || ringerInfo.mode != device.audio.ringerMode) {
        // Confirm with user before reverting the change
        var messageBox = device.notifications.createMessageBox('Undo Volume Change?');
        messageBox.content = 'Ringer volume was changed to ' + device.audio.ringerVolume + 
            '% & mode was changed to \'' + device.audio.ringerMode + '\' on receiving ' + 
            trigger + ' from ' + phoneNumber + '.\nDo you want to change it back to ' + 
            ringerInfo.volume + '% and mode \'' + ringerInfo.mode + '\'?';
        messageBox.buttons = [ 'Yes', 'No' ];
        messageBox.on('Yes', function() {
            // Change device volume to last level & don't update the saved variable
            changeVolume(ringerInfo.mode, ringerInfo.volume, trigger + '.Undo', phoneNumber, true);
        });
        messageBox.show();
    }
    else {
        console.log('Revert Volume: Nothing to change: Check 1: ' + 
            (ringerInfo.volume != device.audio.ringerVolume) + ', Check 2: ' + 
            (ringerInfo.mode != device.audio.ringerMode));
    }
    console.log('Completed: Revert Volume: Current Volume: ' + 
        device.audio.ringerVolume + '%, Mode: ' + device.audio.ringerMode);
}

//  Register callback on sms received event
device.messaging.on('smsReceived', function (sms) {
    console.log('SMS Received: ' + sms.from);
    if (phoneNumbers.indexOf(sms.from) > -1) {
        // Change ring & notification volume: Notify only if volume was changed
        if (changeVolume(ringerMode, ringerVolume, 'SMS', sms.from, false)) {
        
            // Notify user that volume was changed
            var smsNotification = device.notifications.createNotification('Volume changed to ' + ringerVolume + '%');
            smsNotification.content = 'Change on SMS from ' + sms.from + '. Tap to revert.';
            // If user clicks on notification confirm with user regarding reverting the volume
            smsNotification.on('click', function() { 
                revertVolume('SMS', sms.from);
            });
            smsNotification.show();
        }
    }
});

function callStatusChanged(trigger) {
    // Check if volume was changed
    var callInfo = device.localStorage.getItem('callInfo') || { phone: '+1234567890', volumeChanged: false  };
    callInfo = JSON.parse(callInfo);    
    console.log('Started: Call Status Changed: Trigger: ' + trigger + 
        ', Call Info: Changed: ' + callInfo.volumeChanged + ', Phone: ' + callInfo.phone);
    
    // If volume was changed then notify the user
    if (callInfo.volumeChanged) {
        // Stop listening for off-hook & idle phone state
        device.telephony.off('offHook', fnOffHookListener);
        device.telephony.off('idle', fnIdleListener);
        
        // Reset volume change saved state
        device.localStorage.setItem('callInfo', JSON.stringify(
            { phone: '+1234567890', volumeChanged: false }));

        // Notify user that volume was changed
        var callNotification = device.notifications.createNotification('Volume changed to ' + ringerVolume + '%');
        callNotification.content = 'Change on Call from ' + callInfo.phone + '. Tap to revert.';
        // If user clicks on notification confirm with user regarding reverting the volume
        callNotification.on('click', function() {
            revertVolume('Call', callInfo.phone);
        });
        callNotification.show();
    }
    console.log('Completed: Call Status Changed: Current Ringer Volume: ' +
        device.audio.ringerVolume + '% Mode: ' + device.audio.ringerMode);
}

// Register listener on call received event
device.telephony.on('incomingCall', function (signal) {
    console.log('Incoming Call: ' + signal.phoneNumber);
    if (phoneNumbers.indexOf(signal.phoneNumber) > -1) {
        // Change ring & notification volume: Notify user if volume changed
        if (changeVolume(ringerMode, ringerVolume, 'Call', signal.phoneNumber, false)) {
            // Volume was changed: Save info
            var callInfo = { phone: signal.phoneNumber, volumeChanged: true };
            device.localStorage.setItem('callInfo', JSON.stringify(callInfo));
            
            // Wait for phone to get off-hook
            device.telephony.on('offHook', fnOffHookListener = function (signal) { 
                console.log('Phone is Off Hook!');
                callStatusChanged('Off Hook');
            });
            // Wait for phone to be idle again
            device.telephony.on('idle', fnIdleListener = function (signal) { 
                console.log('Phone is Idle!');
                callStatusChanged('Idle');
            });
        }
    }
});

console.log('Completed script: When ' + phoneNumbers[0] + ' calls/texts me, then change the ring volume to ' + ringerVolume + '%.');
